package wci.frontend.pascal.parsers;

import static wci.frontend.pascal.PascalErrorCode.CASE_CONSTANT_REUSED;
import static wci.frontend.pascal.PascalErrorCode.INCOMPATIBLE_TYPES;
import static wci.frontend.pascal.PascalErrorCode.INVALID_CONSTANT;
import static wci.frontend.pascal.PascalErrorCode.MISSING_COLON;
import static wci.frontend.pascal.PascalErrorCode.MISSING_COMMA;
import static wci.frontend.pascal.PascalErrorCode.MISSING_END;
import static wci.frontend.pascal.PascalErrorCode.MISSING_OF;
import static wci.frontend.pascal.PascalErrorCode.MISSING_SEMICOLON;
import static wci.frontend.pascal.PascalTokenType.COLON;
import static wci.frontend.pascal.PascalTokenType.COMMA;
import static wci.frontend.pascal.PascalTokenType.END;
import static wci.frontend.pascal.PascalTokenType.IDENTIFIER;
import static wci.frontend.pascal.PascalTokenType.INTEGER;
import static wci.frontend.pascal.PascalTokenType.MINUS;
import static wci.frontend.pascal.PascalTokenType.OF;
import static wci.frontend.pascal.PascalTokenType.PLUS;
import static wci.frontend.pascal.PascalTokenType.SEMICOLON;
import static wci.frontend.pascal.PascalTokenType.STRING;
import static wci.intermediate.icodeimpl.ICodeKeyImpl.VALUE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.INTEGER_CONSTANT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.SELECT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.SELECT_BRANCH;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.SELECT_CONSTANTS;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.STRING_CONSTANT;

import java.util.EnumSet;
import java.util.HashSet;

import wci.frontend.EofToken;
import wci.frontend.Token;
import wci.frontend.TokenType;
import wci.frontend.pascal.PascalErrorCode;
import wci.frontend.pascal.PascalParserTD;
import wci.frontend.pascal.PascalTokenType;
import wci.intermediate.Definition;
import wci.intermediate.ICodeFactory;
import wci.intermediate.ICodeNode;
import wci.intermediate.SymTabEntry;
import wci.intermediate.SymTabKey;
import wci.intermediate.TypeSpec;
import wci.intermediate.symtabimpl.DefinitionImpl;
import wci.intermediate.symtabimpl.Predefined;
import wci.intermediate.symtabimpl.SymTabKeyImpl;
import wci.intermediate.typeimpl.TypeChecker;
import wci.intermediate.typeimpl.TypeFormImpl;

/**
 * <h1>CaseStatementParser</h1>
 * <p>解析Pascal CASE语句</p>
 * <p>Copyright (c) 2009 by Ronald Mak
 * For instructional purposes only.  No warranties.</p>
 */
public class CaseStatementParser extends StatementParser
{
    public CaseStatementParser(PascalParserTD parent)
    {
        super(parent);
    }
    //一个CASE条件分支的起始同步集合
    private static final EnumSet<PascalTokenType> CONSTANT_START_SET =
        EnumSet.of(IDENTIFIER, INTEGER, PLUS, MINUS, STRING);

    // OF同步集合
    private static final EnumSet<PascalTokenType> OF_SET =
        CONSTANT_START_SET.clone();
    static {
        OF_SET.add(OF);
        OF_SET.addAll(StatementParser.STMT_FOLLOW_SET);
    }
    public ICodeNode parse(Token token)
        throws Exception
    {
    	//干掉开头的CASE
        token = nextToken();  
        ICodeNode selectNode = ICodeFactory.createICodeNode(SELECT);
        //CASE表达式
        ExpressionParser expressionParser = new ExpressionParser(this);
        ICodeNode exprNode = expressionParser.parse(token);
		selectNode.addChild(exprNode);
		TypeSpec exprType = exprNode != null ? exprNode.getTypeSpec()
                : Predefined.undefinedType;
		if (!TypeChecker.isInteger(exprType)
				&& !TypeChecker.isChar(exprType) 
				&& (exprType.getForm() != TypeFormImpl.ENUMERATION))
		{
			errorHandler.flag(token, INCOMPATIBLE_TYPES, this);
		}
		//同步OF
        token = synchronize(OF_SET);
        if (token.getType() == OF) {
            token = nextToken();  // consume the OF
        }
        else {
            errorHandler.flag(token, MISSING_OF, this);
        }
        //分支选项集合
        HashSet<Object> constantSet = new HashSet<Object>();
        while (!(token instanceof EofToken) && (token.getType() != END)) {
            selectNode.addChild(parseBranch(token,exprType, constantSet));
            token = currentToken();
            TokenType tokenType = token.getType();
            // 一个分支以分号';'结束
            if (tokenType == SEMICOLON) {
                token = nextToken();  
            }
            else if (CONSTANT_START_SET.contains(tokenType)) {
                errorHandler.flag(token, MISSING_SEMICOLON, this);
            }
        }
        //CASE expr OF branches END
        if (token.getType() == END) {
            token = nextToken();
        }
        else {
            errorHandler.flag(token, MISSING_END, this);
        }

        return selectNode;
    }

    /**
     * 解析CASE的一个条件分支 CONST(',' CONST)*: STATEMENT;
     * @param token 当前token
     * @param exprType 初始表达式类型
     * @param constantSet 传入的分支常量集合
     * @return SELECT_BRANCH分析子树根节点
     * @throws Exception
     */
    private ICodeNode parseBranch(Token token, TypeSpec exprType, HashSet<Object> constantSet)
        throws Exception
    {
        //每个分支都有一个SELECT_BRANCH节点，constantsNode是第一个孩子
        ICodeNode branchNode = ICodeFactory.createICodeNode(SELECT_BRANCH);
        ICodeNode constantsNode =ICodeFactory.createICodeNode(SELECT_CONSTANTS);
        branchNode.addChild(constantsNode);
        //解析常量列表，每个常量节点是是constantNode的子节点)
        parseConstantList(token, exprType,constantsNode, constantSet);
        //查找分支冒号
        token = currentToken();
        if (token.getType() == COLON) {
            token = nextToken(); 
        }
        else {
            errorHandler.flag(token, MISSING_COLON, this);
        }
        //冒号:后面的语句
        StatementParser statementParser = new StatementParser(this);
        branchNode.addChild(statementParser.parse(token));
        return branchNode;
    }

    //每个常量之后的同步集合
    private static final EnumSet<PascalTokenType> COMMA_SET =
        CONSTANT_START_SET.clone();
    static {
        COMMA_SET.add(COMMA);
        COMMA_SET.add(COLON);
        COMMA_SET.addAll(StatementParser.STMT_START_SET);
        COMMA_SET.addAll(StatementParser.STMT_FOLLOW_SET);
    }

    /**
     * 解析分支的常量列表CONST(',' CONST)*
     * @param token 当前token
     * @param exprType 初始表达式类型
     * @param constantsNode SELECT_CONSTANTS 父节点
     * @param constantSet 分支常量集合，可以根据这个找出重复的
     * @throws Exception
     */
    private void parseConstantList(Token token, TypeSpec exprType, ICodeNode constantsNode,
                                   HashSet<Object> constantSet)
        throws Exception
    {
        // 遍历每一个常量
        while (CONSTANT_START_SET.contains(token.getType())) {
            constantsNode.addChild(parseConstant(token, exprType,constantSet));
            token = synchronize(COMMA_SET);
            // 常量以','隔开
            if (token.getType() == COMMA) {
                token = nextToken(); 
            }
            else if (CONSTANT_START_SET.contains(token.getType())) {
                errorHandler.flag(token, MISSING_COMMA, this);
            }
        }
    }

    /**
     * 解析**一个**分支常量
     * @param token 当前token
     * @param exprType 初始表达式类型
     * @param constantSet 分支常量集合，可以根据这个找出重复的
     * @return the constant 常量节点
     * @throws Exception
     */
    private ICodeNode parseConstant(Token token, TypeSpec exprType, HashSet<Object> constantSet)
        throws Exception
    {
        TokenType sign = null;//符号前缀，正号/负号
        ICodeNode constantNode = null;
        TypeSpec constantType = null; //常量类型
        //先同步一下
        token = synchronize(CONSTANT_START_SET);
        TokenType tokenType = token.getType();
        if ((tokenType == PLUS) || (tokenType == MINUS)) {
            sign = tokenType;
            token = nextToken(); 
        }

        // 解析各种类型常量
        switch ((PascalTokenType) token.getType()) {

            case IDENTIFIER: {//未实现
                constantNode = parseIdentifierConstant(token, sign);
                if (constantNode != null) {
                    constantType = constantNode.getTypeSpec();
                }
                break;
            }

            case INTEGER: {
                constantNode = parseIntegerConstant(token.getText(), sign);
                constantType = Predefined.integerType;
                break;
            }

            case STRING: {
                constantNode =
                    parseCharacterConstant(token, (String) token.getValue(),
                                           sign);
                constantType = Predefined.charType;
                break;
            }

            default: {
                errorHandler.flag(token, INVALID_CONSTANT, this);
                break;
            }
        }

        //分支是不允许重复的，检查一下是否有重复常量
        if (constantNode != null) {
            Object value = constantNode.getAttribute(VALUE);

            if (constantSet.contains(value)) {
                errorHandler.flag(token, CASE_CONSTANT_REUSED, this);
            }
            else {
                constantSet.add(value);
            }
        }
        if (!TypeChecker.areComparisonCompatible(exprType,
                constantType)) {
        	errorHandler.flag(token, INCOMPATIBLE_TYPES, this);
        }
        token = nextToken(); 
        constantNode.setTypeSpec(constantType);
        return constantNode;
    }

    /**
     * 解析标识符常量，并判断类型是否与整数兼容
     * @param token 常量token
     * @param sign 符号token
     * @return 常量节点
     */
    private ICodeNode parseIdentifierConstant(Token token, TokenType sign)
        throws Exception
    {
        ICodeNode constantNode = null;
        TypeSpec constantType = null;

        // 符号表堆栈中查找此常量标识符
        String name = token.getText().toLowerCase();
        SymTabEntry id = symTabStack.lookup(name);

        //如果未定义，直接结束
        if (id == null) {
            id = symTabStack.enterLocal(name);
            id.setDefinition(DefinitionImpl.UNDEFINED);
            id.setTypeSpec(Predefined.undefinedType);
            errorHandler.flag(token, PascalErrorCode.IDENTIFIER_UNDEFINED, this);
            return null;
        }

        Definition defnCode = id.getDefinition();

        // 常量标识符是否符合定义
        if ((defnCode == DefinitionImpl.CONSTANT) || (defnCode == DefinitionImpl.ENUMERATION_CONSTANT)) {
            Object constantValue = id.getAttribute(SymTabKeyImpl.CONSTANT_VALUE);
            constantType = id.getTypeSpec();

            // 与整数兼容才行
            if ((sign != null) && !TypeChecker.isInteger(constantType)) {
                errorHandler.flag(token, INVALID_CONSTANT, this);
            }

            constantNode = ICodeFactory.createICodeNode(INTEGER_CONSTANT);
            constantNode.setAttribute(VALUE, constantValue);
        }

        id.appendLineNumber(token.getLineNumber());

        if (constantNode != null) {
            constantNode.setTypeSpec(constantType);
        }

        return constantNode;
    }

    /**
     * 分支整数常量
     * @param value token字串
     * @param sign 符号位，可以使null表示没有
     * @return 常量节点
     */
    private ICodeNode parseIntegerConstant(String value, TokenType sign)
    {
        ICodeNode constantNode = ICodeFactory.createICodeNode(INTEGER_CONSTANT);
        int intValue = Integer.parseInt(value);

        if (sign == MINUS) {
            intValue = -intValue;
        }

        constantNode.setAttribute(VALUE, intValue);
        return constantNode;
    }

    /**
     * 分支字符常量'K','M','N':之类
     * @param token 当前token
     * @param value 字面文本串，也就是字符串本身
     * @param sign 符号位，有符号位报错
     * @return 常量节点
     */
    private ICodeNode parseCharacterConstant(Token token, String value,
                                             TokenType sign)
    {
        ICodeNode constantNode = null;

        if (sign != null) {
            errorHandler.flag(token, INVALID_CONSTANT, this);
        }
        else {
            if (value.length() == 1) {
                constantNode = ICodeFactory.createICodeNode(STRING_CONSTANT);
                constantNode.setAttribute(VALUE, value);
            }
            else {
                errorHandler.flag(token, INVALID_CONSTANT, this);
            }
        }

        return constantNode;
    }
}
